#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#ifdef _WIN32
	#include <winsock2.h>
#else
	#include "arpa/inet.h"
	#include "unistd.h"
	#include "string.h"
#endif
#include <stdio.h>
#include <cstdint>
#include <thread>

#pragma comment(lib,"ws2_32.lib")

typedef unsigned int SOCKET;

// simulate login service
enum class DataType
{
	LOG_IN, LOGIN_OK, LOGIN_FAILED, LOG_OUT,
	PERSON_INFO,
	REQUEST_ERROR
};

// 1 fisrt get response type, then get request data
struct DataTypePackage {
	DataType data_type;
	unsigned int data_length;
};
// login data
struct Login : public DataTypePackage {

	Login() {
		data_type = DataType::LOG_IN;
		data_length = sizeof(Login);
	}

	char name[128];
	char passwd[128];

};

struct LoginOk : public DataTypePackage {

	LoginOk() {
		data_type = DataType::LOGIN_OK;
		data_length = sizeof(LoginOk);

	}

	char name[128];

};

struct LoginFailed : public DataTypePackage {

	LoginFailed() {
		data_type = DataType::LOGIN_FAILED;
		data_length = sizeof(LoginFailed);

	}

	char msg[128];

};

struct LogOut : public DataTypePackage {

	LogOut() {
		data_type = DataType::LOG_OUT;
		data_length = sizeof(LogOut);

	}

	char name[128];

};

// use struct
struct PersonInfo : public DataTypePackage {

	PersonInfo() {
		data_type = DataType::PERSON_INFO;
		data_length = sizeof(PersonInfo);

		age = 0;
	}

	char name[128];
	unsigned int age;

};

struct RequestError : public DataTypePackage {

	RequestError() {
		data_type = DataType::REQUEST_ERROR;
		data_length = sizeof(RequestError);

	}
	char msg[128];

};

void handle_read_fd(SOCKET _sock) {
	char response[1024] = {};
	// data type
	recv(_sock, response, 1024, 0);
	DataTypePackage* data_type;
	data_type = (DataTypePackage*)response;

	if (data_type->data_type != DataType::REQUEST_ERROR) {
		if (data_type->data_type == DataType::LOGIN_OK) {

			LoginOk* loginOk;
			loginOk = (LoginOk*)response;

			printf("login ok, name: %s\n", loginOk->name);
		}
		else if (data_type->data_type == DataType::PERSON_INFO) {

			PersonInfo* person;
			person = (PersonInfo*)response;
			printf("name: %s, age: %d\n", person->name, person->age);
		}
		else {
			printf("operator error\n");
		}
	}
	else {
		printf("request error\n");
	}
}

bool cmd_stop = false;

void cmd_fun(SOCKET _sock) {
	while (!cmd_stop) {

		char cmdBuf[1024] = {};

		printf("input cmd: \n");
		scanf("%s", cmdBuf);

		int n = 0;
		if (0 == strcmp("logout", cmdBuf)) {

			LogOut logOut = {};
			send(_sock, (char*)&logOut, sizeof(LogOut), 0);

			printf("logout\n");
			cmd_stop = true;
			break;

		}
		else if (0 == strcmp("login", cmdBuf)) {

			char nameBuf[128] = {};
			char passwdBuf[128] = {};
			printf("input name: \n");
			scanf("%s", nameBuf);
			printf("input passwd: \n");
			scanf("%s", passwdBuf);

			printf("pass: %s\n", passwdBuf);

			Login login = {};
			strcpy(login.name, nameBuf);
			strcpy(login.passwd, passwdBuf);

			int n = send(_sock, (char*)&login, sizeof(Login), 0);
			printf("send len: %d\n", n);

		}
		else if (0 == strcmp("person_info", cmdBuf)) {
			PersonInfo person = {};
			send(_sock, (char*)&person, sizeof(PersonInfo), 0);
		}
	}
}

int main() {

#ifdef _WIN32
	//��ʼ��WSA
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	if (WSAStartup(sockVersion, &wsaData) != 0)
	{
		return 0;
	}
#endif
	// 1. ����socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6666);// ��С��ת��������ʹ�õĴ����
#ifdef _WIN32
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// server ip
#else
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");// server ip
#endif
	// 2. connect server
	connect(_sock, (sockaddr*)&addr, sizeof(addr));

	fd_set readfd;
	fd_set writefd;
	fd_set exceptfd;

	FD_ZERO(&readfd);
	FD_ZERO(&writefd);
	FD_ZERO(&exceptfd);

	FD_SET(_sock, &readfd);
	FD_SET(_sock, &writefd);
	FD_SET(_sock, &exceptfd);

	std::thread cmd_thread(cmd_fun, _sock);

	while (!cmd_stop) {
	
		timeval t;
		t.tv_sec = 60;
		t.tv_usec = 0;
	
		// importtant !!!
		FD_ZERO(&readfd);
		FD_SET(_sock, &readfd);

		printf("waiting select...\n");
		int n = select(FD_SETSIZE, &readfd, 0, 0, &t);

		// if (readfd.fd_count > 0) {
			if (FD_ISSET(_sock, &readfd)){
				printf("handle read fd\n");
				handle_read_fd(_sock);

			}
		//}

	}

	// 5. close server socket.
#ifdef _WIN32
	closesocket(_sock);
	WSACleanup();
#else
	close(_sock);
#endif

	return 0;
}

